/*
 * @Date: 2023-02-18 11:26:32
 * @LastEditors: Zireael 1193875164@qq.com
 * @LastEditTime: 2023-03-23 18:42:52
 * @FilePath: \A19\backend\src\utils\Classes\Live.ts
 */
import { users,liveStudent } from './../../shared/types/user';
import { ObjectId } from "mongodb"
import { Global } from '../../Global/index';
import { WsConnection } from 'tsrpc';
import { Wsserver } from '../..';
import { ServiceType } from '../../shared/protocols/serviceProto';
import { inflate } from 'zlib';
import { courses } from '../../shared/types/courses';
import { MsgUpdateStatus } from '../../shared/protocols/AMessages/Game/MsgUpdateStatus';

export class Live{
    static lives = new Array<Live>
    static teacherConnCount = 2

    talks:Array<{
        user:users,
        talk:string,
        type:'connect' | 'disconnect' |'talk'
    }>
    players = new Array<player>()
    interval:NodeJS.Timeout | undefined

    courseId:ObjectId
    host:{
        teacher:users
        connection:WsConnection
    }
    students:Array<liveStudent>
    courseInfo:courses | undefined
    
    connectionMap:Map<string,{
        node:WsConnection,
        afters:Array<{
            node:WsConnection,
            after:WsConnection | undefined
        }>
    }>

    constructor(courseId:ObjectId,host:{
        teacher:users
        connection:WsConnection
    }){
        this.courseId = courseId
        this.host = host

        this.students = new Array<liveStudent>()
        this.stuInit(courseId)
        this.connectionMap = new Map<string,{
            node:WsConnection,
            afters:Array<{
                node:WsConnection,
                after:WsConnection
            }>
        }>
        this.talks = new Array<{
            user:users,
            talk:string,
            type:'connect' | 'disconnect' | 'talk'
        }>()
        Live.lives.push(this)
        // this.gameSync()
    }

    async stuInit(courseId:ObjectId){
        Global.collection('selectCourses').aggregate([
            {
                $match:{
                    course:courseId
                }
            },
            {
                $lookup:{
                    from:'users',
                    localField: 'student',
                    foreignField:'_id',
                    as:'student'
                }
            }
        ])
        .toArray()
        .then(async stus =>{
            console.log(stus);
            this.students = stus.map((stu) =>{
                return {
                    ...stu.student[0],
                    watchingTime:0,
                    status:'offline', //online | offline
                    connection:undefined
                } as liveStudent
            });
            console.log(this.students);
            // console.log(this.students.map(stu => stu.name));
            this.courseInfo = await Global.collection('courses').findOne({
                _id:courseId
            }) as courses;
            (this.host.connection as WsConnection<ServiceType>).sendMsg('AMessages/Live/StuList',{
                students:this.students,
                course:this.courseInfo as courses
            })
        })
        .catch(error =>{
            console.log(error);
        })
    }

    getTarget(student:ObjectId,conn:WsConnection):WsConnection{
        //计算target
        let target:WsConnection
        if(this.connectionMap.size < Live.teacherConnCount){
            target = this.host.connection
            this.connectionMap.set(student.toString(),{
                node:conn,
                afters:new Array<{
                    node:WsConnection,
                    after:WsConnection | undefined
                }>
            })
        }
        else{
            const chains = [...this.connectionMap.values()]
            const { node,afters:chain } = chains.reduce((previousValue, currentValue, currentIndex, array)=>{
                if(previousValue.afters.length <= currentValue.afters.length) return previousValue
                else return currentValue
            }, chains[0]) 
            // console.log(chain);
            //如果链上没有节点，直接连接node
            if(chain.length === 0){
                target = node
                chain.push({
                    node:conn,
                    after:undefined
                })
            }
            //链上有节点
            else{
                //寻找after为空的节点
                console.log('寻找after为空的节点');
                const room = chain.find(v => v.after === undefined)
                //如果有空的 加入这个节点
                if(room !== undefined){
                    console.log('//如果有空的 加入这个节点');
                    target = room.node
                    room.after = conn
                }
                //如果没空的 连接上一个节点并创建新节点
                else{
                    console.log('如果没空的 连接上一个节点并创建新节点');
                    const lastestNode = chain.at(-1)
                    target = lastestNode!.node
                    chain.push({
                        node:conn,
                        after:undefined
                    })
                }
            }
            
        }

        return target
    }

    async sendTalk(user:users,kotona:string,type:'connect' | 'disconnect' |'talk'){
        const talk = {
            user:user,
            talk:kotona,
            type
        }
        this.talks.push(talk)

        const connections = new Array<WsConnection>
        connections.push(this.host.connection)
        this.students.forEach(s =>{
            if(s.connection){
                connections.push(s.connection)
            }
        })

        Wsserver.broadcastMsg('AMessages/Communicate/ReceiveTalk',{
            talk
        },connections)
    }

    endLive(){
        //通知直播中的学生直播已结束
        const stus = this.students.filter(s => s.connection !== undefined)
        console.log(stus);
        
        Wsserver.broadcastMsg('AMessages/Live/LiveEnd',{},stus.map(s => s.connection) as WsConnection<ServiceType>[])
        //从直播列表移除
        const index = Live.lives.findIndex(live => live.courseId.equals(this.courseId))
        Live.lives.splice(index,1);

        if(this.interval) clearInterval(this.interval)
    }

    //#region 
    // gameSync(){
    //     this.interval = setInterval(()=>{
    //         if(this.players.length === 0) return

    //         Wsserver.broadcastMsg('AMessages/Game/PlayersStatus',{
    //             players:this.players
    //         },this.players.map(p => p.connection))
    //     },)
    // }
    //#endregion

}

export interface player extends MsgUpdateStatus{
    connection:WsConnection<ServiceType>
    
}

